/**
 ******************************************************************************
 * @file app_snake_file.c
 * @author GX (2524913364@qq.com)
 * @brief  这是游戏“方块贪吃蛇”的辅助文件，处理游戏相关的文字和文件操作，包括了有些介绍界面
 *          和保存玩家游戏记录的排行榜。
 * @version 1.0.1 
 * @date 2023-09-02
 * 
 * @copyright Copyright (c) 2022
 * 
 ******************************************************************************
 *----------------------------------------------------------------------------*
 *  Remark         : Description                                              *
 *----------------------------------------------------------------------------*
 *  Change History :                                                          *
 *  <Date>     | <Version> | <Author>       | <Description>                   *
 *----------------------------------------------------------------------------*
 *  2022-07-29 | 1.0.0     | GX             | Create file                     *
 *----------------------------------------------------------------------------*
 *  2023-09-02 | 1.0.1     | GX             | fixbug,close file               *
 *----------------------------------------------------------------------------*
 *                                                                            *
 ******************************************************************************
 */


/*----------------------------- start of file -------------------------------*/
#include "app_snake_file.h"
#include "lcd.h"
#include "fonts.h"
#include "touch_screen.h"


/* 静态的函数声明--------------------------------------------------------- */
static void get_playerData (void);
static void write_playerData (RankData_t buf);
static void rank_playerData (void);

/* 全局变量定义---------------------------------------------------------- */
u_int8_t player_num = 1;           /* 玩家数 */
SnakePlayer_t       player[1];

/*******************************************************************************
 * 
 *                                  游戏介绍界面
 * 
*******************************************************************************/

/**
 * @brief 显示游戏介绍的界面
 * 
 * @param fb_base 显存映射的基地址
 * @param fd      显示屏的文件描述符
 * @param vinfo   显示屏可变属性的指针* 
 */
void snake_intro_interface (int *fb_base, int fd, struct fb_var_screeninfo *vinfo)
{
  /* 显示介绍界面----------------------------------- */
  view_block++;
  lcd_show_bmp (LCD_DISPLAY(fb_base, view_block), S_INTRO_PATH, 0, 0);
  LCD_SET_DISPLAY(fd, vinfo, view_block);

  bool close_intro = false;
  TOUCH *tc = malloc(sizeof(TOUCH));

  while (1)
  {
    get_tcState(tc);
    if (!tc->response)
    {
      switch (tc->state)
      {
      case STATE_SINGLE_CLICK:
        close_intro = true;
        response_over();
        break;
      
      default:
        response_over();
        break;
      }
    }

    if (close_intro)
    {
      break;  
    }
    
  }
 
  free(tc);

}

/**
 * @brief 询问玩家是否保存玩家信息的界面
 * 
 * @param fb_base 显存映射的基地址
 * @param fd      显示屏的文件描述符
 * @param vinfo   显示屏可变属性的指针* 
 */
void snake_save_interface (int *fb_base, int fd, struct fb_var_screeninfo *vinfo)
{
  /* 显示界面 --------------------------------*/
  /* 标题 */
  lcd_rectangle (LCD_DISPLAY(fb_base, view_block), SI_SAVE_X_START, SI_SAVE_Y_START, 
                    SI_SAVE_WIDTH, SI_SAVE_HEIGHT, SI_SAVE_COLOR);
  DispString_EN (LCD_DISPLAY(fb_base, view_block), SI_SAVE_TITEL_X, SI_SAVE_TITEL_Y, 
                    SI_SAVE_TITEL);
  /* “Yes” */
  lcd_rectangle (LCD_DISPLAY(fb_base, view_block), SI_YES_X_START, SI_YES_Y_START, 
                    SI_YES_WIDTH, SI_YES_HEIGHT, SI_YES_COLOR);
  DispString_EN (LCD_DISPLAY(fb_base, view_block), SI_TES_TXET_X, SI_TES_TXET_Y, 
                    SI_YES_TXET);
  /* No */
  lcd_rectangle (LCD_DISPLAY(fb_base, view_block), SI_NO_X_START, SI_NO_Y_START, 
                    SI_NO_WIDTH, SI_NO_HEIGHT, SI_NO_COLOR);
  DispString_EN (LCD_DISPLAY(fb_base, view_block), SI_NO_TXET_X, SI_NO_TXET_Y, 
                    SI_NO_TEXT);

  /* 判断操作 -------------------------------- */
  TOUCH *tc = malloc(sizeof(TOUCH));
  while (1)
  {
    get_tcState(tc);
    if (!tc->response)
    {
      int32_t x = tc->x_last;
      int32_t y = tc->y_last;  
      if (tc->state == STATE_SINGLE_CLICK)
      {
        response_over();
        if ((x > SI_YES_X_START) && (x < SI_YES_X_END) 
              && (y > SI_YES_Y_START) && (y < SI_YES_Y_END) )
        {/* 保存成绩 */
          printf ("save\n");
          get_playerData();
          rank_playerData ();
          return ;
        }
        else if ((x > SI_NO_X_START) && (x < SI_NO_X_END) 
                  && (y > SI_NO_Y_START) && (y < SI_NO_Y_END) )
        {/* 直接退出不保存 */
          printf ("don't save\n");
          return ; 
        }
      }
      else
      {
        response_over();
      }
    }
    
  }
}

/**
 * @brief 显示排行榜
 * 
 * @param fb_base 显存映射的基地址
 * @param fd      显示屏的文件描述符
 * @param vinfo   显示屏可变属性的指针* 
 */
void snake_rank_interface (int *fb_base, int fd, struct fb_var_screeninfo *vinfo)
{
  /* 显示字符相关配置------------------------------- */
  /* 要先把进来前的字体配置记住，在结束后要设置回来 */
  bool useBack;
  u_int32_t TextColor, BackColor;
  sFONT *fonts;
  fonts = LCD_GetFont();
  LCD_GetColors(&TextColor, &BackColor, &useBack);
  LCD_SetFont(&Font32x48); 
  LCD_SetColors(LCD_PURPLE, 0x00, false);

  /* 读取排行榜信息（最多读取前五名）------------------ */
  FILE *fp;
  fp = fopen("./data/rankdata.txt", "r");
  if (fp == NULL)
  {
    perror ("open rankdata.txt");
    return;
  }

  int32_t data_num = 0;
  data_num = lseek(fp->_fileno, 0, SEEK_END)/sizeof(RankData_t);
  fseek(fp, 0, SEEK_SET); 
  printf("data num : %d\n", data_num);
  data_num = (data_num > 5) ? 5 : data_num;

  RankData_t *data_buf = malloc (data_num*sizeof(RankData_t));
  for (int32_t i = 0; i < data_num; i++)
  {
    printf("read data\n");

    fread ((data_buf+i), sizeof(RankData_t), 1, fp);
    printf("%d %s ", (data_buf+i)->score, (data_buf+i)->name);
    printf("%d-%d-%d %d:%d:%d\n", (data_buf+i)->datetime.tm_year, (data_buf+i)->datetime.tm_mon,
              (data_buf+i)->datetime.tm_mday, (data_buf+i)->datetime.tm_hour, (data_buf+i)->datetime.tm_min,
                (data_buf+i)->datetime.tm_sec);
  }
  fclose(fp);


  /* 显示排行榜界面----------------------------------- */
  view_block++;
  
  lcd_rectangle (LCD_DISPLAY(fb_base, view_block), SI_RANK_X_START, SI_RANK_Y_START,
                      SI_RANK_WIDTH, SI_RANK_HEIGHT, SI_RANK_COLOR);
  DispString_EN (LCD_DISPLAY(fb_base, view_block), SI_RANK_TITEL_X, SI_RANK_TITEL_Y,
                      SI_RANK_TITEL);
  LCD_SetFont(&Font16x24); 
  LCD_SetColors(LCD_BLACK, 0x00, false);
  char buf[50] = {0}; /* 存储排行榜信息 */
  /* 显示表头 */
  sprintf (buf, "%9s %5s %9s %8s", "name", "score", "date", "time");
  DispString_EN (LCD_DISPLAY(fb_base, view_block), SI_RANK_LISTHEAD_X, SI_RANK_LISTHEAD_Y, buf);
  
  for (int32_t i = 0; i < data_num; i++)
  {
    sprintf (buf, "%9s %4d %4d-%2d-%2d %2d:%2d:%2d", (data_buf+i)->name, (data_buf+i)->score, 
              (data_buf+i)->datetime.tm_year, (data_buf+i)->datetime.tm_mon, (data_buf+i)->datetime.tm_mday,
                (data_buf+i)->datetime.tm_hour, (data_buf+i)->datetime.tm_min, (data_buf+i)->datetime.tm_sec);
    DispString_EN (LCD_DISPLAY(fb_base, view_block), SI_RANK_LIST_X, 
                    (SI_RANK_LIST_Y+i*SI_RANK_LIST_HEIGHT), buf);
  }

  LCD_SET_DISPLAY(fd, vinfo, view_block);

  bool close_intro = false;
  TOUCH *tc = malloc(sizeof(TOUCH));

  while (1)
  {
    get_tcState(tc);
    if (!tc->response)
    {
      switch (tc->state)
      {
      case STATE_SINGLE_CLICK:
        close_intro = true;
        response_over();
        break;
      
      default:
        response_over();
        break;
      }
    }

    if (close_intro)
    {
      break;  
    }
    
  }
 
  free(tc);
  free(data_buf);

  /* 恢复字体设置 */
  LCD_SetFont(fonts); 
  LCD_SetColors(TextColor, BackColor, useBack);
}

static void write_playerData (RankData_t buf)
{
  /* 将玩家信息写入文件 */
  FILE *fp1;
  FILE *fp2;
  fp1 = fopen("./data/rankdata.txt", "a+"); /* 记录用来处理的数据 */
  if (fp1 == NULL)
  {
    perror ("open rankdata.txt");
    return;
  }
  fp2 = fopen("./data/rank.txt", "a+");     /* 记录让人看得直观的数据 */
  if (fp2 == NULL)
  {
    fclose(fp1);
    perror ("open rank.txt");
    return;
  }
  
  printf("score %d\n", player[0].score);
  
  fwrite (&buf, sizeof(RankData_t), 1, fp1);
  fprintf (fp2, "%9s %4d %4d-%2d-%2d %2d:%2d:%2d\n", buf.name, buf.score, 
            buf.datetime.tm_year, buf.datetime.tm_mon,buf.datetime.tm_mday, 
                buf.datetime.tm_hour, buf.datetime.tm_min, buf.datetime.tm_sec);

  fclose(fp1);
  fclose(fp2);
}

static void rank_playerData (void)
{
  RankData_t new_data;
  new_data.score = player[0].score;
  strcpy(new_data.name, player[0].name);
  memcpy(&new_data.datetime, &player[0].datetime, sizeof(struct tm));
  printf("%d %s ", new_data.score, new_data.name);
  printf("%d-%d-%d %d:%d:%d\n", new_data.datetime.tm_year, new_data.datetime.tm_mon,
            new_data.datetime.tm_mday, new_data.datetime.tm_hour, new_data.datetime.tm_min,
              new_data.datetime.tm_sec);

  /* 打开排行榜文件 */
  FILE *fp;
  fp = fopen("./data/rankdata.txt", "r");
  if (fp == NULL)
  {
    perror ("open rankdata.txt");
    return;
  }

  int32_t data_num = 0;
  data_num = lseek(fp->_fileno, 0, SEEK_END)/sizeof(RankData_t);
  fseek(fp, 0, SEEK_SET); 
  printf("data num : %d\n", data_num);
 
  /* 将历史数据取出来 */
  RankData_t *data_buf = malloc (data_num*sizeof(RankData_t));
  for (int32_t i = 0; i < data_num; i++)
  {
    printf("read data\n");

    fread ((data_buf+i), sizeof(RankData_t), 1, fp);
    printf("%d %s ", (data_buf+i)->score, (data_buf+i)->name);
    printf("%d-%d-%d %d:%d:%d\n", (data_buf+i)->datetime.tm_year, (data_buf+i)->datetime.tm_mon,
              (data_buf+i)->datetime.tm_mday, (data_buf+i)->datetime.tm_hour, (data_buf+i)->datetime.tm_min,
                (data_buf+i)->datetime.tm_sec);
  }
  fclose(fp);

  /* 清空原来文件的数据 */
  FILE *fp1;
  FILE *fp2;
  fp1 = fopen("./data/rankdata.txt", "w"); /* 记录用来处理的数据 */
  if (fp1 != NULL)
  {
    fclose(fp1);
  }
  fp2 = fopen("./data/rank.txt", "w");     /* 记录让人看得直观的数据 */
  if (fp1 != NULL)
  {
    fclose(fp2);
  }

  /* 排序 */
  int32_t write_num = 0;  /* 记录已经写入了几组历史数据 */
  /* 先写分数大于新的玩家数据的历史数据 */
  for (int32_t i = 0; i < data_num; i++)
  {
    if ((data_buf+i)->score > new_data.score)
    {
      write_playerData(*(data_buf+i));
      write_num++;
    }
    else
    {
      write_playerData(new_data);
      break;
    }
  }
  /* 将剩下的数据写入 */
  for (int32_t i = write_num; i < data_num; i++)
  {
    write_playerData(*(data_buf+i));
  }
  
  

  free(data_buf);
}

static void get_playerData (void)
{
  /* 获取时间 */
  time_t time_tmp = time(NULL);
  struct tm *tmp;
  tmp = localtime(&time_tmp);
  memcpy(&player[0].datetime, tmp, sizeof(struct tm));
  player[0].datetime.tm_year += 1900;
  player[0].datetime.tm_mon  += 1;
  
  printf("%d-%d-%d %d:%d:%d\n", player[0].datetime.tm_year, player[0].datetime.tm_mon,
            player[0].datetime.tm_mday, player[0].datetime.tm_hour, player[0].datetime.tm_min,
              player[0].datetime.tm_sec);

  /* 获取玩家名字 */
  memset(player[0].name, ' ', sizeof(player[0].name));
  printf("What's your name?(Up to 9 letters)");
  scanf("%s", player[0].name);
  printf("your name:%s\n", player[0].name);
  
}

/*------------------------------ end of file --------------------------------*/

